﻿//===============================================================================
// Jammer.NET Development Library
// Jmr.Entity.Poco.Silverlight
//===============================================================================
// Copyright © Junaid Fakhruddin, Matthew Leedham and Randolph Cabral.  
// All rights reserved.
//===============================================================================
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using Jmr.Compression;
using Jmr.Serialization;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Media.Animation;
using System.Linq.Expressions;
using System.Xml.Linq;
using System.Text;
using System.Globalization;

namespace Jmr.Entity.Poco.Silverlight
{
	public class EntitySessionFactory
	{
		public static IEntitySessionProxy CreateSession(string uri, string mappingAssemblyFullName, string mappingFullName)
		{
			return new EntitySessionProxy(uri, mappingAssemblyFullName, mappingFullName);
		}
	}

	public delegate void FetchSetCompletedEventHandler(object sender, FetchSetEventArgs e);
	public delegate void FetchSingleCompletedEventHandler(object sender, FetchSingleEventArgs e);
	public delegate void FetchResultsCompletedEventHandler(object sender, FetchResultsEventArgs e);
	public delegate void InsertCompletedEventHandler(object sender, InsertEventArgs e);
	public delegate void InsertBatchCompletedEventHandler(object sender, InsertBatchEventArgs e);
	public delegate void UpdateCompletedEventHandler(object sender, UpdateEventArgs e);
	public delegate void UpdateBatchCompletedEventHandler(object sender, UpdateBatchEventArgs e);
	public delegate void DeleteCompletedEventHandler(object sender, DeleteEventArgs e);
	public delegate void DeleteBatchCompletedEventHandler(object sender, DeleteEventArgs e);
	public delegate void ExceptionEventHandler(JmrSerializationException exception);
	public delegate void ExecuteQueryCompletedEventHandler(object sender, DynamicTable e);
	public delegate void ExecuteMDXQueryCompletedEventHandler(object sender, DynamicTable e);
	public delegate void AsQueryableLoadedEventHandler(object sender, IQueryable e);

	public class FetchSetEventArgs
	{
		public IEnumerable Results { get; set; }
	}

	public class FetchResultsEventArgs
	{
		public IEnumerable Results { get; set; }
	}

	public class FetchSingleEventArgs
	{
		public object Result { get; set; }
	}

	public class InsertEventArgs : RecordsAffectedEventArgs
	{
		public object Result { get; set; }
	}

	public class InsertBatchEventArgs : RecordsAffectedEventArgs
	{
		public IEnumerable Results { get; set; }
	}

	public class UpdateEventArgs : RecordsAffectedEventArgs
	{
		public object Result { get; set; }
	}

	public class UpdateBatchEventArgs : RecordsAffectedEventArgs
	{
		public IEnumerable Results { get; set; }
	}

	public class DeleteEventArgs : RecordsAffectedEventArgs { }

	public class RecordsAffectedEventArgs
	{
		public int RecordsAffected { get; set; }
	}

	public interface IEntitySessionProxy
	{
		event FetchSetCompletedEventHandler OnFetchSetCompleted;
		event FetchResultsCompletedEventHandler OnFetchResultsCompleted;
		event FetchSingleCompletedEventHandler OnFetchSingleCompleted;
		event InsertCompletedEventHandler OnInsertCompleted;
		event InsertBatchCompletedEventHandler OnInsertBatchCompleted;
		event UpdateCompletedEventHandler OnUpdateCompleted;
		event UpdateBatchCompletedEventHandler OnUpdateBatchCompleted;
		event DeleteCompletedEventHandler OnDeleteCompleted;
		event DeleteBatchCompletedEventHandler OnDeleteBatchCompleted;
		event ExecuteQueryCompletedEventHandler OnExecuteQueryCompleted;
		event ExecuteMDXQueryCompletedEventHandler OnExecuteMDXQueryCompleted;
    ObservableCollection<TEntity> FetchSet<TEntity>(object entityarg);
    TEntity FetchSingle<TEntity>(object entityarg);
		void ExecuteDynamicQuery(DatabaseQuery query);
		void ExecuteDynamicMDXQuery(DatabaseQuery query);
		void Insert<TEntity>(TEntity entityref);
		void Update<TEntity>(TEntity entityref);
		void Delete<TEntity>(TEntity entityref);
		void InsertBatch<TEntity>(IEnumerable<TEntity> entities);
		void UpdateBatch<TEntity>(IEnumerable<TEntity> entities);
		void DeleteBatch<TEntity>(IEnumerable<TEntity> entities);
		void FetchResults<TEntity>(IQueryable expression);
		IQueryable<TEntity> AsQueryable<TEntity>();
	}

	public class EntitySessionProxy : IEntitySessionProxy
	{
		public event FetchSetCompletedEventHandler OnFetchSetCompleted;
		public event FetchResultsCompletedEventHandler OnFetchResultsCompleted;
		public event FetchSingleCompletedEventHandler OnFetchSingleCompleted;
		public event InsertCompletedEventHandler OnInsertCompleted;
		public event InsertBatchCompletedEventHandler OnInsertBatchCompleted;
		public event UpdateCompletedEventHandler OnUpdateCompleted;
		public event UpdateBatchCompletedEventHandler OnUpdateBatchCompleted;
		public event DeleteCompletedEventHandler OnDeleteCompleted;
		public event DeleteBatchCompletedEventHandler OnDeleteBatchCompleted;
		public event ExecuteQueryCompletedEventHandler OnExecuteQueryCompleted;
    public event ExecuteMDXQueryCompletedEventHandler OnExecuteMDXQueryCompleted;
    public static event ExceptionEventHandler OnEntityException;
		
		internal EntitySessionProxy(string uri, string mappingAssemblyFullName, string mappingFullName)
		{
			_uristring = uri.ToLower();
			_uri = new Uri(_uristring);
			_mappingAssemblyFullName = mappingAssemblyFullName;
			_mappingFullName = mappingFullName;
			_binaryformatter = new BinaryEntityFormatter();
			_syncContext = SynchronizationContext.Current;
      _objectCache = new List<object>();
		}

		private SynchronizationContext _syncContext;
		private string _uristring = null;
		private string _mappingAssemblyFullName = null;
		private string _mappingFullName = null;
		private Uri _uri = null;
		private BinaryEntityFormatter _binaryformatter = null;
    private List<object> _objectCache = null;

		private WebRequest GetWebRequest(Action action, string entityassemblyname, string entityfullname, object entityarg)
		{
			var webrequest = HttpWebRequest.Create(_uri);
			webrequest.Headers["Culture"] = System.Globalization.CultureInfo.CurrentCulture.ToString();
			webrequest.Headers[EntitySessionHeaderKeys.EntitySessionAction] = action.ToString();
			webrequest.Headers[EntitySessionHeaderKeys.MappingAssemblyFullName] = _mappingAssemblyFullName;
			webrequest.Headers[EntitySessionHeaderKeys.MappingFullName] = _mappingFullName;
			webrequest.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName] = entityassemblyname;
			webrequest.Headers[EntitySessionHeaderKeys.EntityFullName] = entityfullname;
			webrequest.Headers[EntitySessionHeaderKeys.EntityArgFullName] = (entityarg == null) ? "NULL" : entityarg.GetType().FullName;

			if (entityarg is IEnumerable && entityarg.GetType().IsGenericType)
				webrequest.Headers[EntitySessionHeaderKeys.EntityArgFullName] = (entityarg == null) ? "NULL" : entityarg.GetType().GetGenericArguments()[0].FullName;

			webrequest.Method = "POST";
			return webrequest;
		}

		private WebRequest GetWebRequest(Action action)
		{
			var webrequest = HttpWebRequest.Create(_uri);
			webrequest.Headers["Culture"] = System.Globalization.CultureInfo.CurrentCulture.ToString();
			webrequest.Headers[EntitySessionHeaderKeys.EntitySessionAction] = action.ToString();
			webrequest.Method = "POST";
			return webrequest;
		}

		public void DoAction(Action action, DatabaseQuery queryArgs)
		{
			var webRequest = GetWebRequest(action);
			var entityarg = (queryArgs == null) ? new byte[0] : _binaryformatter.Serialize(queryArgs).ToArray();
			webRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = webRequest });
		}

		public void DoAction<TEntity>(Action action, IQueryable expression)
		{
			DatabaseQueryExpression queryExpression = new DatabaseQueryExpression();
			ExpressionSerializer serializer = new ExpressionSerializer();
			StringWriter sw = new StringWriter();
			serializer.Serialize(expression.Expression).Save(sw);
			queryExpression.QueryXml = sw.ToString();
			var webRequest = GetWebRequest(action, typeof(TEntity).Assembly.FullName, typeof(TEntity).FullName, null);
			var entityarg = (queryExpression == null) ? new byte[0] : _binaryformatter.Serialize(queryExpression).ToArray();
			webRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = webRequest });
		}

		public void DoAction<TEntity>(Action action, object entityArg)
		{
			var webRequest = GetWebRequest(action, typeof(TEntity).Assembly.FullName, typeof(TEntity).FullName, entityArg);
			var entityarg = (entityArg == null) ? new byte[0] : _binaryformatter.Serialize(entityArg).ToArray();
			webRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = webRequest });
		}

		private void ReadCallback(IAsyncResult asynchronousResult)
		{
			var argRequest = (EntityArgRequest)asynchronousResult.AsyncState;
			var entityarg = argRequest.EntityArg;
			var request = argRequest.Request;
			var postStream = request.EndGetRequestStream(asynchronousResult);
			postStream.Write(entityarg, 0, entityarg.Length);
			postStream.Flush();
			postStream.Close();
			var asyncresult = request.BeginGetResponse(new AsyncCallback(BinaryActionCompleted), request);
		}

		public void RaiseException(JmrSerializationException exception)
		{
			if (exception.ExceptionType.ToLower().Contains("safariheaderbug"))
				return;

			if (OnEntityException != null)
				OnEntityException(exception);
		}

		void BinaryActionCompleted(IAsyncResult result)
		{
			HttpWebRequest request = (HttpWebRequest)result.AsyncState;
			var action = (Action)Enum.Parse(typeof(Action), request.Headers["entitysessionaction"], true);
			switch (action)
			{
				case Action.FetchSet:
					if (_syncContext == null)
						FetchSet_BinaryActionCompleted(result);
					else
						_syncContext.Post(FetchSet_BinaryActionCompleted, result);
					break;
				case Action.FetchSingle:
					if (_syncContext == null)
						FetchSingle_BinaryActionCompleted(result);
					else
						_syncContext.Post(FetchSingle_BinaryActionCompleted, result);
					break;
				case Action.Insert:
					if (_syncContext == null)
						Insert_BinaryActionCompleted(result);
					else
						_syncContext.Post(Insert_BinaryActionCompleted, result);
					break;
				case Action.InsertBatch:
					if (_syncContext == null)
						InsertBatch_BinaryActionCompleted(result);
					else
						_syncContext.Post(InsertBatch_BinaryActionCompleted, result);
					break;
				case Action.Update:
					if (_syncContext == null)
						Update_BinaryActionCompleted(result);
					else
						_syncContext.Post(Update_BinaryActionCompleted, result);
					break;
				case Action.UpdateBatch:
					if (_syncContext == null)
						UpdateBatch_BinaryActionCompleted(result);
					else
						_syncContext.Post(UpdateBatch_BinaryActionCompleted, result);
					break;
				case Action.Delete:
					if (_syncContext == null)
						Delete_BinaryActionCompleted(result);
					else
						_syncContext.Post(Delete_BinaryActionCompleted, result);
					break;
				case Action.DeleteBatch:
					if (_syncContext == null)
						DeleteBatch_BinaryActionCompleted(result);
					else
						_syncContext.Post(DeleteBatch_BinaryActionCompleted, result);
					break;
				case Action.ExecuteQuery:
					if (_syncContext == null)
						ExecuteQuery_BinaryActionCompleted(result);
					else
						_syncContext.Post(ExecuteQuery_BinaryActionCompleted, result);
					break;
				case Action.ExecuteMDXQuery:
					if (_syncContext == null)
						ExecuteMDXQuery_BinaryActionCompleted(result);
					else
						_syncContext.Post(ExecuteMDXQuery_BinaryActionCompleted, result);
					break;
				case Action.FetchResults:
					if (_syncContext == null)
						FetchResults_BinaryActionCompleted(result);
					else
						_syncContext.Post(FetchResults_BinaryActionCompleted, result);
					break;
			}
		}

		HttpWebRequest GetRequest(IAsyncResult result)
		{
			return (HttpWebRequest)result.AsyncState;
		}

		HttpWebResponse GetResponse(IAsyncResult result)
		{
			var request = GetRequest(result);
			return (HttpWebResponse)request.EndGetResponse(result);
		}

		Stream GetResponseStream(IAsyncResult result)
		{
			return GetResponse(result).GetResponseStream();
		}

		int ParseRecordsAffectedAsyncResult(IAsyncResult result)
		{
			var stream = GetResponseStream((IAsyncResult)result);
			return ParseRecordsAffectedStream(stream);
		}

		int ParseRecordsAffectedStream(Stream stream)
		{
			var streamreader = new StreamReader(stream);
			return Convert.ToInt32(streamreader.ReadToEnd());
		}

		public ObservableCollection<TEntity> FetchSet<TEntity>(object entityarg)
		{
      var retval = new ObservableCollection<TEntity>();
      _objectCache.Add(retval);
			DoAction<TEntity>(Action.FetchSet, entityarg);
      return retval;
		}

		private void FetchSet_BinaryActionCompleted(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName];
			var entityfullname = request.Headers[EntitySessionHeaderKeys.EntityFullName];
			var listtype = typeof(List<>).MakeGenericType(ParseType(assemblyfullname, entityfullname));
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(listtype, uncompresssed);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnFetchSetCompleted != null)
				OnFetchSetCompleted(this, new FetchSetEventArgs { Results = (IEnumerable)deserializedObj });
		}

		private void FetchResults_BinaryActionCompleted(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName];
			var entityfullname = request.Headers[EntitySessionHeaderKeys.EntityFullName];
			
			var listtype = typeof(List<>).MakeGenericType(ParseType(assemblyfullname, entityfullname));
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(listtype, uncompresssed);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnFetchResultsCompleted != null)
				OnFetchResultsCompleted(this, new FetchResultsEventArgs { Results = (IEnumerable)deserializedObj });
		}

		public void ExecuteDynamicQuery(DatabaseQuery query)
		{
			DoAction(Action.ExecuteQuery, query);
		}

		public void FetchResults<TEntity>(IQueryable expression)
		{
			DoAction<TEntity>(Action.FetchResults, expression);
		}

		public IQueryable<TEntity> AsQueryable<TEntity>()
		{
			var provider = new QueryProvider();
			IQueryable<TEntity> queryable = new Query<TEntity>(provider);
			return queryable;
		}

		public void ExecuteDynamicMDXQuery(DatabaseQuery query)
		{
			DoAction(Action.ExecuteMDXQuery, query);
		}

		private void ExecuteQuery_BinaryActionCompleted(object state)
		{
			var deserializedObj = GetDeserializedObject(state);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnExecuteQueryCompleted != null)
				OnExecuteQueryCompleted(this, (DynamicTable)deserializedObj);
		}

		private void ExecuteMDXQuery_BinaryActionCompleted(object state)
		{
			var deserializedObj = GetDeserializedObject(state);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}
			if (OnExecuteMDXQueryCompleted != null)
				OnExecuteMDXQueryCompleted(this, (DynamicTable)deserializedObj);
		}

		private object GetDeserializedObject(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(typeof(DynamicTable), uncompresssed);

			return deserializedObj;
		}

		public TEntity FetchSingle<TEntity>(object entityarg)
		{
      var retval = Activator.CreateInstance<TEntity>();
      _objectCache.Add(retval);
			DoAction<TEntity>(Action.FetchSingle, entityarg);
      return retval;
		}

		private void FetchSingle_BinaryActionCompleted(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName];
			var entityfullname = request.Headers[EntitySessionHeaderKeys.EntityFullName];
			var entitytype = ParseType(assemblyfullname, entityfullname);
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(entitytype, uncompresssed);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnFetchSingleCompleted != null)
				OnFetchSingleCompleted(this, new FetchSingleEventArgs { Result = deserializedObj });
		}

		public void Insert<TEntity>(TEntity entityref)
		{
			DoAction<TEntity>(Action.Insert, entityref);
		}

		void Insert_BinaryActionCompleted(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName];
			var entityfullname = request.Headers[EntitySessionHeaderKeys.EntityFullName];
			var entitytype = ParseType(assemblyfullname, entityfullname);
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(entitytype, uncompresssed);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnInsertCompleted != null)
				OnInsertCompleted(this, new InsertEventArgs { RecordsAffected = 1, Result = deserializedObj });
		}

		public void Update<TEntity>(TEntity entityref)
		{
      DoAction<TEntity>(Action.Update, entityref);
		}

		void Update_BinaryActionCompleted(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName];
			var entityfullname = request.Headers[EntitySessionHeaderKeys.EntityFullName];
			var entitytype = ParseType(assemblyfullname, entityfullname);
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(entitytype, uncompresssed);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnUpdateCompleted != null)
				OnUpdateCompleted(this, new UpdateEventArgs { RecordsAffected = 1, Result = deserializedObj });
		}

		public void Delete<TEntity>(TEntity entityref)
		{
      DoAction<TEntity>(Action.Delete, entityref);
		}

		void Delete_BinaryActionCompleted(object state)
		{
			if (OnDeleteCompleted != null)
				OnDeleteCompleted(this, new DeleteEventArgs { RecordsAffected = ParseRecordsAffectedAsyncResult((IAsyncResult)state) });
		}

		public void InsertBatch<TEntity>(IEnumerable<TEntity> entities)
		{
      DoAction<TEntity>(Action.InsertBatch, entities);
		}

		void InsertBatch_BinaryActionCompleted(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName];
			var entityfullname = request.Headers[EntitySessionHeaderKeys.EntityFullName];
			var listtype = typeof(List<>).MakeGenericType(ParseType(assemblyfullname, entityfullname));
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(listtype, uncompresssed);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			var listresult = (IList)deserializedObj;

			if (OnInsertBatchCompleted != null)
				OnInsertBatchCompleted(this, new InsertBatchEventArgs { RecordsAffected = listresult.Count, Results = (IEnumerable)deserializedObj });
		}

		public void UpdateBatch<TEntity>(IEnumerable<TEntity> entities)
		{
      DoAction<TEntity>(Action.UpdateBatch, entities);
		}

		void UpdateBatch_BinaryActionCompleted(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers[EntitySessionHeaderKeys.EntityAssemblyFullName];
			var entityfullname = request.Headers[EntitySessionHeaderKeys.EntityFullName];
			var listtype = typeof(List<>).MakeGenericType(ParseType(assemblyfullname, entityfullname));
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Jmr.Compression.Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(listtype, uncompresssed);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			var listresult = (IList)deserializedObj;

			if (OnUpdateBatchCompleted != null)
				OnUpdateBatchCompleted(this, new UpdateBatchEventArgs { RecordsAffected = listresult.Count, Results = (IEnumerable)deserializedObj });
		}

		public void DeleteBatch<TEntity>(IEnumerable<TEntity> entities)
		{
      DoAction<TEntity>(Action.DeleteBatch, entities);
		}

		void DeleteBatch_BinaryActionCompleted(object state)
		{
			if (OnDeleteBatchCompleted != null)
				OnDeleteBatchCompleted(this, new DeleteEventArgs { RecordsAffected = ParseRecordsAffectedAsyncResult((IAsyncResult)state) });
		}

		protected object GetInstance(string assemblyname, string fullname)
		{
			object retval = default(object);
			retval = Activator.CreateInstance(ParseType(assemblyname, fullname));
			return retval;
		}

		protected Type ParseType(string assemblyfullname, string entityfullname)
		{
			Type retval = default(Type);
			retval = System.Reflection.Assembly.Load(assemblyfullname).GetType(entityfullname);
			return retval;
		}
	}

	public class EntityArgRequest
	{
		public WebRequest Request { get; set; }
		public byte[] EntityArg { get; set; }
		public object Results { get; set; }
	}

	public class QueryProvider : IQueryProvider
	{
		public QueryProvider()
		{
		}

		public IQueryable<S> CreateQuery<S>(Expression expression)
		{
			return new Query<S>(this, expression);
		}

		public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
		{
			Type elementType = TypeHelper.GetElementType(expression.Type);
			try
			{
				return (IQueryable)Activator.CreateInstance(typeof(Query<>).MakeGenericType(elementType), new object[] { this, expression });
			}
			catch (TargetInvocationException tie)
			{
				throw tie.InnerException;
			}
		}

		public S Execute<S>(Expression expression)
		{
			return default(S);// (S)this.Execute(expression);
		}

		public object Execute(Expression expression)
	{	
			return null;// this.Execute(expression);
		}

	}

	public class Query<T> : IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable, IOrderedQueryable<T>, IOrderedQueryable
	{
		IQueryProvider provider;
		Expression expression;

		public Query(IQueryProvider provider)
			: this(provider, null)
		{
		}

		public Query(IQueryProvider provider, Type staticType)
		{
			if (provider == null)
			{
				throw new ArgumentNullException("Provider");
			}
			this.provider = provider;
			this.expression = staticType != null ? Expression.Constant(this, staticType) : Expression.Constant(this);
		}

		public Query(QueryProvider provider, Expression expression)
		{
			if (provider == null)
			{
				throw new ArgumentNullException("Provider");
			}
			if (expression == null)
			{
				throw new ArgumentNullException("expression");
			}
			if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type))
			{
				throw new ArgumentOutOfRangeException("expression");
			}
			this.provider = provider;
			this.expression = expression;
		}

		public Expression Expression
		{
			get { return this.expression; }
		}

		public Type ElementType
		{
			get { return typeof(T); }
		}

		public IQueryProvider Provider
		{
			get { return this.provider; }
		}

		public IEnumerator<T> GetEnumerator()
		{
			return ((IEnumerable<T>)this.provider.Execute(this.expression)).GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return ((IEnumerable)this.provider.Execute(this.expression)).GetEnumerator();
		}

		public override string ToString()
		{
			if (this.expression.NodeType == ExpressionType.Constant &&
					((ConstantExpression)this.expression).Value == this)
			{
				return "Query(" + typeof(T) + ")";
			}
			else
			{
				return this.expression.ToString();
			}
		}
	}

	public static class TypeHelper
	{
		public static Type FindIEnumerable(Type seqType)
		{
			if (seqType == null || seqType == typeof(string))
				return null;
			if (seqType.IsArray)
				return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
			if (seqType.IsGenericType)
			{
				foreach (Type arg in seqType.GetGenericArguments())
				{
					Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
					if (ienum.IsAssignableFrom(seqType))
					{
						return ienum;
					}
				}
			}
			Type[] ifaces = seqType.GetInterfaces();
			if (ifaces != null && ifaces.Length > 0)
			{
				foreach (Type iface in ifaces)
				{
					Type ienum = FindIEnumerable(iface);
					if (ienum != null) return ienum;
				}
			}
			if (seqType.BaseType != null && seqType.BaseType != typeof(object))
			{
				return FindIEnumerable(seqType.BaseType);
			}
			return null;
		}

		public static Type GetSequenceType(Type elementType)
		{
			return typeof(IEnumerable<>).MakeGenericType(elementType);
		}

		public static Type GetElementType(Type seqType)
		{
			Type ienum = FindIEnumerable(seqType);
			if (ienum == null) return seqType;
			return ienum.GetGenericArguments()[0];
		}

		public static bool IsNullableType(Type type)
		{
			return type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
		}

		public static bool IsNullAssignable(Type type)
		{
			return !type.IsValueType || IsNullableType(type);
		}

		public static Type GetNonNullableType(Type type)
		{
			if (IsNullableType(type))
			{
				return type.GetGenericArguments()[0];
			}
			return type;
		}

		public static Type GetNullAssignableType(Type type)
		{
			if (!IsNullAssignable(type))
			{
				return typeof(Nullable<>).MakeGenericType(type);
			}
			return type;
		}

		public static ConstantExpression GetNullConstant(Type type)
		{
			return Expression.Constant(null, GetNullAssignableType(type));
		}

		public static Type GetMemberType(MemberInfo mi)
		{
			FieldInfo fi = mi as FieldInfo;
			if (fi != null) return fi.FieldType;
			PropertyInfo pi = mi as PropertyInfo;
			if (pi != null) return pi.PropertyType;
			EventInfo ei = mi as EventInfo;
			if (ei != null) return ei.EventHandlerType;
			MethodInfo meth = mi as MethodInfo;  // property getters really
			if (meth != null) return meth.ReturnType;
			return null;
		}

		public static object GetDefault(Type type)
		{
			bool isNullable = !type.IsValueType || TypeHelper.IsNullableType(type);
			if (!isNullable)
				return Activator.CreateInstance(type);
			return null;
		}

		public static bool IsReadOnly(MemberInfo member)
		{
			switch (member.MemberType)
			{
				case MemberTypes.Field:
					return (((FieldInfo)member).Attributes & FieldAttributes.InitOnly) != 0;
				case MemberTypes.Property:
					PropertyInfo pi = (PropertyInfo)member;
					return !pi.CanWrite || pi.GetSetMethod() == null;
				default:
					return true;
			}
		}

		public static bool IsInteger(Type type)
		{
			Type nnType = GetNonNullableType(type);
			switch (Type.GetTypeCode(type))
			{
				case TypeCode.SByte:
				case TypeCode.Int16:
				case TypeCode.Int32:
				case TypeCode.Int64:
				case TypeCode.Byte:
				case TypeCode.UInt16:
				case TypeCode.UInt32:
				case TypeCode.UInt64:
					return true;
				default:
					return false;
			}
		}
	}

}



